/*
 *    Copyright 2022 The DSMS Authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.dsms.modules.storagepool.service.impl;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ceph.rados.IoCTX;
import com.ceph.rados.Rados;
import com.ceph.rados.exceptions.ErrorCode;
import com.ceph.rados.exceptions.RadosException;
import com.ceph.rbd.Rbd;
import com.ceph.rbd.RbdException;
import com.ceph.rbd.RbdImage;
import com.dsms.common.constant.*;
import com.dsms.common.exception.DsmsEngineException;
import com.dsms.common.model.PageParam;
import com.dsms.common.taskmanager.TaskContext;
import com.dsms.common.taskmanager.model.AsyncStep;
import com.dsms.common.taskmanager.model.AsyncTask;
import com.dsms.common.taskmanager.model.Step;
import com.dsms.common.taskmanager.model.Task;
import com.dsms.common.taskmanager.service.IStepService;
import com.dsms.common.taskmanager.service.ITaskService;
import com.dsms.common.util.FilterUtil;
import com.dsms.common.util.PageUtil;
import com.dsms.dfsbroker.filesystem.api.FileSystemApi;
import com.dsms.dfsbroker.filesystem.model.remote.FsLsResponse;
import com.dsms.dfsbroker.node.api.NodeApi;
import com.dsms.dfsbroker.node.model.Device;
import com.dsms.dfsbroker.node.model.Node;
import com.dsms.dfsbroker.node.model.dto.NodeDto;
import com.dsms.dfsbroker.node.model.remote.OsdDfResult;
import com.dsms.dfsbroker.node.model.remote.ResponseMon;
import com.dsms.dfsbroker.node.service.INodeService;
import com.dsms.dfsbroker.osd.crushmap.api.CrushmapApi;
import com.dsms.dfsbroker.osd.crushmap.model.remote.CrushmapBucket;
import com.dsms.dfsbroker.osd.ecprofile.api.EcProfileApi;
import com.dsms.dfsbroker.osd.ecprofile.model.remote.RemoteEcProfile;
import com.dsms.dfsbroker.rbd.service.IRbdService;
import com.dsms.dfsbroker.rbd.service.RbdWrapper;
import com.dsms.dfsbroker.storagepool.api.StoragePoolApi;
import com.dsms.dfsbroker.storagepool.model.StoragePool;
import com.dsms.dfsbroker.storagepool.model.dto.StoragePoolCreateDTO;
import com.dsms.dfsbroker.storagepool.model.dto.StoragePoolDeleteDTO;
import com.dsms.dfsbroker.storagepool.model.dto.StoragePoolDiskManageDTO;
import com.dsms.dfsbroker.storagepool.model.dto.StoragePoolNodeManageDTO;
import com.dsms.dfsbroker.storagepool.model.remote.DfResponse;
import com.dsms.dfsbroker.storagepool.model.remote.ResponseStoragePool;
import com.dsms.dfsbroker.storagepool.service.IStoragePoolService;
import com.dsms.modules.storagepool.model.dto.StoragePoolRbdInfoDto;
import com.dsms.modules.storagepool.model.vo.StoragePoolListVO;
import com.dsms.modules.util.RadosSingleton;
import com.dsms.modules.util.RemoteCallUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.dsms.dfsbroker.rbd.model.Rbd.RBD_META_DATA_BYTES;

@Service
@Slf4j
public class StoragePoolImpl implements IStoragePoolService {

    private static final int PG_NUM_ZERO = 0;
    private static final int REMOVED_HOST_NUM = 1;
    private static final int AVAILABLE_POOL_MIN_USED_OSD_NUM_IN_ONE_HOST = 1;
    private static final String DEFAULT_POOL = "device_health_metrics";
    private static final String NOT_ALLOWED_REMOVE_OSD_REASON = "存储池需要冗余模式数量的节点磁盘对数据进行平衡和备份，移除磁盘操作可能会导致存储池不可用和数据丢失，请通过删除存储池或添加大于冗余模式数量磁盘的方式移除磁盘";
    private static final String TASK_ALREADY_EXIST = "Task already exist,task message: {}";
    private static final String TASK_CREATE_FAILED = "Task create failed exist,task message: {}";
    private static final String NOT_BE_EMPTY = "storagePoolNodeManageDto can not be empty";

    StoragePoolApi storagePoolApi;

    FileSystemApi fileSystemApi;

    CrushmapApi crushmapApi;

    EcProfileApi ecProfileApi;

    NodeApi nodeApi;

    private ITaskService taskService;

    private IStepService stepService;

    @Lazy
    @Autowired
    private TaskContext taskContext;

    @Autowired
    @Lazy
    private INodeService nodeService;

    public StoragePoolImpl(StoragePoolApi storagePoolApi, FileSystemApi fileSystemApi, CrushmapApi crushmapApi, EcProfileApi ecProfileApi, NodeApi nodeApi, ITaskService taskService, IStepService stepService) {
        this.storagePoolApi = storagePoolApi;
        this.fileSystemApi = fileSystemApi;
        this.crushmapApi = crushmapApi;
        this.ecProfileApi = ecProfileApi;
        this.nodeApi = nodeApi;
        this.taskService = taskService;
        this.stepService = stepService;
    }

    @Override
    public StoragePool get(String poolName) {
        if (ObjectUtils.isEmpty(poolName)) {
            throw new RuntimeException("poolName cannot be null or empty");
        }

        Rados rados = RadosSingleton.INSTANCE.getRados();
        ResponseStoragePool resPool = null;
        try (IoCTX ioctx = rados.ioCtxCreate(poolName)) {
            int id = (int) ioctx.getId();
            resPool = storagePoolApi.getStoragePoolByPoolId(RemoteCallUtil.generateRemoteRequest(), id);
        } catch (RadosException e) {
            throw new RuntimeException(ErrorCode.getErrorMessage(e.getReturnValue()));
        } catch (Throwable e) {
            log.error("get pool from dsms-storage fail,fail message:{}", e.getMessage(), e);
            throw new DsmsEngineException(e, ResultCode.POOL_LISTPOOL_ERROR);
        }
        String redundantMode = getPoolRedundantMode(resPool);
        StoragePool storagePool = new StoragePool(resPool.getPoolId(), resPool.getPoolName(), resPool.getType(), resPool.getPgNum(), resPool.getPgpNum(), redundantMode);

        //get pools' usedSize and totalSize
        DfResponse dfResponse = getDfResponse();

        //get the infomation of file system
        List<FsLsResponse> fsLsResponse = getFsLsResponses();

        //pool should have at least three osd in different node
        boolean isAvailable = isPoolAvailable(storagePool.getPoolName());
        storagePool.setIsAvailable(isAvailable);

        //get the rbd info of this storage pool
        StoragePoolRbdInfoDto rbdInfo;
        if (storagePool.getType() == StoragePoolTypeEnum.ERASURE.getCode()) {
            rbdInfo = getErasurePoolRbdInfo(rados, storagePool.getPoolName());
        } else {
            rbdInfo = getReplicaPoolRbdInfo(rados, storagePool.getPoolName(), isAvailable);
        }

        //get storage pool's rbd total size
        String[] rbdList = rbdInfo.getRbdList();
        if (!ArrayUtil.isEmpty(rbdList)) {
            storagePool.setRbdList(rbdList);
        }

        //add pool capacity info into storagePool
        long rbdTotalSize = rbdInfo.getRbdTotalSize();
        if (!ObjectUtils.isEmpty(dfResponse.getPools())) {
            List<DfResponse.PoolsDTO> dfPools = dfResponse.getPools();
            for (DfResponse.PoolsDTO dfPool : dfPools) {
                if (dfPool.getId() == storagePool.getPoolId()) {
                    storagePool.setCapacity(dfPool);
                    //Each rbd needs to reserve 100M metadata space
                    if (storagePool.getType() == StoragePoolTypeEnum.REPLICATED.getCode()) {
                        storagePool.setUsedSize(storagePool.getUsedSize() + (rbdInfo.getRbdList().length * RBD_META_DATA_BYTES));
                        storagePool.setRbdAvailableCapacity(storagePool.getTotalSize() - rbdTotalSize - (rbdInfo.getRbdList().length * RBD_META_DATA_BYTES));
                    } else {
                        storagePool.setRbdAvailableCapacity(storagePool.getTotalSize() - rbdTotalSize);
                    }
                }
            }

        }

        //add pool filesystem info into storagePool
        if (!ObjectUtils.isEmpty((fsLsResponse))) {
            for (FsLsResponse fs : fsLsResponse) {
                if (fs.getMetadataPoolId() == storagePool.getPoolId() || fs.getDataPoolIds().contains(storagePool.getPoolId())) {
                    storagePool.setFileSystem(fs.getName());
                }
            }
        }

        return storagePool;
    }

    @Override
    public List<StoragePool> list() {
        List<StoragePool> storagePoolList = new ArrayList<>();
        //get the basic infomation of pools
        List<ResponseStoragePool> responseStoragePool;
        try {
            responseStoragePool = storagePoolApi.getStoragePoolList(RemoteCallUtil.generateRemoteRequest());
        } catch (DsmsEngineException e) {
            log.error("get pools from dsms-storage fail,fail message:{}", e.getMessage(), e);
            throw new DsmsEngineException(e, ResultCode.POOL_LISTPOOL_ERROR);
        } catch (Throwable e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        if (ObjectUtils.isEmpty(responseStoragePool)) {
            return storagePoolList;
        }

        //construct storagePoolList
        responseStoragePool.stream().forEach(resPool -> {
            String redundantMode = getPoolRedundantMode(resPool);
            StoragePool storagePool = new StoragePool(resPool.getPoolId(), resPool.getPoolName(), resPool.getType(), resPool.getPgNum(), resPool.getPgpNum(), redundantMode);
            if (!Objects.equals(storagePool.getPoolName(), DEFAULT_POOL)) {
                storagePoolList.add(storagePool);
            }
        });

        return storagePoolList;
    }

    private String getPoolRedundantMode(ResponseStoragePool responsePool) {
        if (Objects.equals(responsePool.getType(), StoragePoolTypeEnum.REPLICATED.getCode())) {
            return responsePool.getCrushOsdNum() + "副本";
        } else {
            RemoteEcProfile resEcProfile = null;
            try {
                resEcProfile = ecProfileApi.getEcProfile(RemoteCallUtil.generateRemoteRequest(), responsePool.getPoolName());
            } catch (Throwable e) {
                log.error("get ecProfile error :{}", e.getMessage(), e);
                throw new RuntimeException(e);
            }
            return resEcProfile.getK() + "+" + resEcProfile.getM();
        }
    }

    private DfResponse getDfResponse() {
        DfResponse dfResponse;
        try {
            dfResponse = storagePoolApi.getDf(RemoteCallUtil.generateRemoteRequest());
        } catch (DsmsEngineException e) {
            log.error("get pools' capacity info from dsms-storage fail,fail message:{}", e.getMessage(), e);
            throw new DsmsEngineException(e, ResultCode.POOL_GETPOOLCAPACITY_ERROR);
        } catch (Throwable e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return dfResponse;
    }

    private List<FsLsResponse> getFsLsResponses() {
        List<FsLsResponse> fsLsResponse;
        try {
            fsLsResponse = fileSystemApi.getFsLs(RemoteCallUtil.generateRemoteRequest());
        } catch (DsmsEngineException e) {
            log.error("get filesystem info from dsms-storage fail,fail message:{}", e.getMessage(), e);
            throw new DsmsEngineException(e, ResultCode.POOL_GETFILESYSTEM_ERROR);
        } catch (Throwable e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return fsLsResponse;
    }

    /*
     * get all storage volumes by storage pool name
     * */
    private StoragePoolRbdInfoDto getReplicaPoolRbdInfo(Rados rados, String poolName, boolean isAvailable) {
        long rbdTotalSize = 0;
        String[] rbdList = new String[0];
        List<String> rbdListTemp = new ArrayList<>();
        if (isAvailable) {
            try ( // get storage pool ioctx
                  IoCTX ioctx = rados.ioCtxCreate(poolName)) {
                int poolId = (int) ioctx.getId();
                // get storage pool's rbd name list
                RbdWrapper rbdWrapper = new RbdWrapper(ioctx);
                rbdList = rbdWrapper.list();
                rbdTotalSize = Arrays.stream(rbdList).parallel().mapToLong(rbdName -> {
                    try (RbdImage rbdImage = rbdWrapper.open(rbdName)) {
                        String newName = String.format("%s/%s", poolName, rbdName);
                        rbdListTemp.add(newName);
                        int dataPoolId = rbdWrapper.getDataPoolId(rbdName);
                        //partitioned capacity excluding erasure coded pools(rbd's data pool)
                        if (poolId != dataPoolId) {
                            return 0;
                        }
                        return rbdImage.stat().size;
                    } catch (RbdException e) {
                        log.error("get rbd info from dsms-storage fail,fail message:{}", e.getMessage(), e);
                        throw new DsmsEngineException(e, ResultCode.RBD_GETRBDINFO_ERROR);
                    }
                }).sum();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        rbdList = rbdListTemp.toArray(new String[0]);
        return new StoragePoolRbdInfoDto(rbdTotalSize, rbdList);
    }

    /*
     * get the storage volume created based on the erasure coded pool by its name
     * */
    private StoragePoolRbdInfoDto getErasurePoolRbdInfo(Rados rados, String erasurePoolName) {
        long rbdTotalSize = 0;
        String[] rbdList;
        List<String> rbdListTemp = new ArrayList<>();
        try {
            int erasurePoolId = (int) rados.poolLookup(erasurePoolName);
            String[] pools = rados.poolList();
            for (String pool : pools) {
                try (IoCTX ioctx = rados.ioCtxCreate(pool)) {
                    boolean isAvailable = isPoolAvailable(pool);
                    if (isAvailable) {
                        RbdWrapper rbdWrapper = new RbdWrapper(ioctx);
                        List<String> rbds = List.of(rbdWrapper.list());
                        List<String> erasurePoolRbdNames = rbds.stream().filter(rbd -> {
                                    try {
                                        return rbdWrapper.getDataPoolId(rbd) == erasurePoolId;
                                    } catch (RbdException e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                        ).collect(Collectors.toList());
                        long sum = erasurePoolRbdNames.stream().parallel().mapToLong(rbd -> {
                            try (RbdImage rbdImage = new Rbd(ioctx).open(rbd)) {
                                return rbdImage.stat().size;
                            } catch (RbdException e) {
                                throw new RuntimeException(e);
                            }
                        }).sum();
                        rbdTotalSize += sum;
                        List<String> newRbdNames = erasurePoolRbdNames.stream().map(eName -> String.format("%s/%s", pool, eName)).collect(Collectors.toList());
                        rbdListTemp.addAll(newRbdNames);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        } catch (RadosException e) {
            throw new RuntimeException(e);
        }
        rbdList = rbdListTemp.toArray(new String[0]);
        return new StoragePoolRbdInfoDto(rbdTotalSize, rbdList);
    }

    @Override
    public Page<StoragePool> list(PageParam pageParam) {
        //list all basic storage pool
        List<StoragePool> list = list();

        //filter storage pool by name
        if (pageParam instanceof StoragePoolListVO) {
            StoragePoolListVO storagePoolListVO = (StoragePoolListVO) pageParam;
            list = list.stream()
                    .filter(pool -> FilterUtil.fuzzy(storagePoolListVO.getPoolName(), pool.getPoolName()))
                    .collect(Collectors.toList());
        }

        //page the list
        Page<StoragePool> pageData = PageUtil.getPageData(list, pageParam.getPageNo(), pageParam.getPageSize());
        list = pageData.getRecords();

        //add the additional filed to storage pool
        //get pools' usedSize and totalSize
        DfResponse dfResponse = getDfResponse();

        //get the infomation of file system
        List<FsLsResponse> fsLsResponse = getFsLsResponses();

        //add pool capacity info into storagePool
        if (!ObjectUtils.isEmpty(dfResponse.getPools())) {
            List<DfResponse.PoolsDTO> dfPools = dfResponse.getPools();
            Rados rados = RadosSingleton.INSTANCE.getRados();
            list.parallelStream().forEach(storagePool -> dfPools.parallelStream()
                    .filter(poolsDTO -> poolsDTO.getId().equals(storagePool.getPoolId()))
                    .forEach(poolsDTO -> {
                        //pool should have at least three osd in different node
                        boolean isAvailable = isPoolAvailable(storagePool.getPoolName());
                        storagePool.setIsAvailable(isAvailable);
                        StoragePoolRbdInfoDto rbdInfo;
                        if (storagePool.getType() == StoragePoolTypeEnum.ERASURE.getCode()) {
                            rbdInfo = getErasurePoolRbdInfo(rados, storagePool.getPoolName());
                        } else {
                            rbdInfo = getReplicaPoolRbdInfo(rados, storagePool.getPoolName(), isAvailable);
                        }
                        String[] rbdList = rbdInfo.getRbdList();
                        if (!ArrayUtil.isEmpty(rbdList)) {
                            storagePool.setRbdList(rbdList);
                        }

                        long rbdTotalSize = rbdInfo.getRbdTotalSize();
                        storagePool.setCapacity(poolsDTO);
                        //Each rbd needs to reserve 100M metadata space
                        if (storagePool.getType() == StoragePoolTypeEnum.REPLICATED.getCode()) {
                            storagePool.setUsedSize(storagePool.getUsedSize() + (rbdInfo.getRbdList().length * RBD_META_DATA_BYTES));
                            storagePool.setRbdAvailableCapacity(storagePool.getTotalSize() - rbdTotalSize - (rbdInfo.getRbdList().length * RBD_META_DATA_BYTES));
                        } else {
                            storagePool.setRbdAvailableCapacity(storagePool.getTotalSize() - rbdTotalSize);
                        }
                    })
            );
        }

        List<StoragePool> listWithFs = list;
        //add pool filesystem info into storagePool
        if (!ObjectUtils.isEmpty((fsLsResponse))) {
            fsLsResponse.stream().forEach(fs -> listWithFs.stream()
                    .filter(pool -> pool.getPoolId() == fs.getMetadataPoolId() || fs.getDataPoolIds().contains(pool.getPoolId()))
                    .forEach(pool -> pool.setFileSystem(fs.getName())));
        }

        List<StoragePool> finalList = listWithFs;
        //filter storage pool by other query condition
        if (pageParam instanceof StoragePoolListVO) {
            StoragePoolListVO storagePoolListVO = (StoragePoolListVO) pageParam;
            if (Objects.equals(storagePoolListVO.getPoolType(), StoragePoolTypeEnum.REPLICATED.getCode())) {
                finalList = finalList.stream().filter(item -> item.getType() == StoragePoolTypeEnum.REPLICATED.getCode()).collect(Collectors.toList());
            } else if (Objects.equals(storagePoolListVO.getPoolType(), StoragePoolTypeEnum.ERASURE.getCode())) {
                finalList = finalList.stream().filter(item -> item.getType() == StoragePoolTypeEnum.ERASURE.getCode()).collect(Collectors.toList());
            }

            if (!Objects.isNull(storagePoolListVO.getFileSystem())) {
                if (ObjectUtils.isEmpty(storagePoolListVO.getFileSystem())) {
                    finalList = finalList.stream().filter(item -> Objects.isNull(item.getFileSystem())).collect(Collectors.toList());
                } else {
                    finalList = finalList.stream().filter(item -> Objects.equals(storagePoolListVO.getFileSystem(), item.getFileSystem())).collect(Collectors.toList());
                }
            }

            if (!Objects.isNull(storagePoolListVO.getRbd())) {
                if (ObjectUtils.isEmpty(storagePoolListVO.getRbd())) {
                    finalList = finalList.stream().filter(item -> ObjectUtils.isEmpty(item.getRbdList())).collect(Collectors.toList());
                }
            }

            if (!Objects.isNull(storagePoolListVO.getIsAvailable())) {
                finalList = finalList.stream().filter(item -> FilterUtil.matches(storagePoolListVO.getIsAvailable(), item.getIsAvailable()) && item.getRbdAvailableCapacity() > 0).collect(Collectors.toList());
            }
        }

        pageData.setRecords(finalList);
        return pageData;
    }

    @Override
    public NodeDto listUnusedDisk(String poolName, String nodeName) {
        //get current node's device info
        Node nodeInfo = nodeService.getNodeInfo(nodeName);
        //collect the unused osd
        List<Device> device = nodeInfo.getDevices().stream().filter(n -> !ObjectUtils.nullSafeEquals(n.getOsdId(), null)
                && !ObjectUtils.nullSafeEquals(n.getUsedPool(), poolName)).collect(Collectors.toList());
        return new NodeDto(nodeInfo.getNodeId(), nodeInfo.getNodeName(), nodeInfo.getPoolUsedDevice(), nodeInfo.getNodeUsedDevice(), device);
    }

    @Override
    public List<NodeDto> listUsedDisk(String poolName) {
        List<String> nodeNames = new ArrayList<>();
        List<NodeDto> nodeDtoList = new ArrayList<>();
        try {
            //get current pool's host bucket in crushmap
            CrushmapBucket crushmapBuckets = crushmapApi.getCrushmapBuckets(RemoteCallUtil.generateRemoteRequest());
            for (CrushmapBucket.BucketsDTO bucket : crushmapBuckets.getBuckets()) {
                //host bucketName construct with poolName + BUCKET_NAME_CONNECTOR("_") + nodeName. example:pool1_node1
                //ceph also create a bucket named bucketName + AUTO_GENERATED_BUCKET_NAME_CONNECTOR("~") + "hdd"/"ssd", it's useless for dsms-engine,so we should ignore them
                if (bucket.getName().startsWith(poolName + CrushmapConst.BUCKET_NAME_CONNECTOR) && !bucket.getName().contains(CrushmapConst.AUTO_GENERATED_BUCKET_NAME_CONNECTOR)) {
                    //get node's name
                    String nodeName = bucket.getName().split(CrushmapConst.BUCKET_NAME_CONNECTOR)[1];
                    nodeNames.add(nodeName);
                }
            }
        } catch (Throwable e) {
            log.error("get pool's bucket error:" + e.getMessage());
            throw new RuntimeException(e);
        }

        for (String nodeName : nodeNames) {
            //get every single node's device info
            Node nodeInfo = nodeService.getNodeInfo(nodeName);
            //collect the used osd
            List<Device> device = nodeInfo.getDevices().stream().filter(n -> ObjectUtils.nullSafeEquals(n.getUsedPool(), poolName)).collect(Collectors.toList());
            NodeDto nodeDto = new NodeDto(nodeInfo.getNodeId(), nodeInfo.getNodeName(), device);
            nodeDtoList.add(nodeDto);
        }
        return nodeDtoList;
    }

    @Override
    public boolean create(StoragePoolCreateDTO storagePoolCreateDTO) {
        if (ObjectUtils.isEmpty(storagePoolCreateDTO)) {
            log.error("storagePoolCreateDTO can not be empty");
            return false;
        }

        String poolType = StoragePoolTypeEnum.getPoolType(storagePoolCreateDTO.getPoolType());
        String taskMessage = TaskTypeEnum.CREATE_POOL.getName() + ":" + storagePoolCreateDTO.getPoolName() + ", 存储池类型:" + poolType;
        if (!taskContext.validateTask(TaskTypeEnum.CREATE_POOL, taskMessage)) {
            log.warn(TASK_ALREADY_EXIST, taskMessage);
            throw new DsmsEngineException(ResultCode.TASK_EXIST);
        }
        Task task = new AsyncTask(
                TaskTypeEnum.CREATE_POOL.getName(),
                TaskTypeEnum.CREATE_POOL.getType(),
                TaskStatusEnum.QUEUE.getStatus(),
                taskMessage,
                JSONUtil.toJsonStr(storagePoolCreateDTO));

        if (taskService.save(task)) {
            return createStepTask(task.getId(), storagePoolCreateDTO.getPoolName(), StoragePoolTypeEnum.getPoolType(storagePoolCreateDTO.getPoolType()));
        }

        log.error(TASK_CREATE_FAILED, JSONUtil.toJsonStr(task));
        return false;
    }

    @Override
    public boolean addNode(StoragePoolNodeManageDTO storagePoolNodeManageDto) {
        if (ObjectUtils.isEmpty(storagePoolNodeManageDto)) {
            log.error(NOT_BE_EMPTY);
            return false;
        }

        validateAddNode(storagePoolNodeManageDto.getPoolName(), storagePoolNodeManageDto.getNodeNames());

        String taskMessage = TaskTypeEnum.POOL_ADD_NODE.getName() + ":" + storagePoolNodeManageDto.getPoolName();
        if (!taskContext.validateTask(TaskTypeEnum.POOL_ADD_NODE, taskMessage)) {
            log.warn(TASK_ALREADY_EXIST, taskMessage);
            throw new DsmsEngineException(ResultCode.TASK_EXIST);
        }
        Task task = new AsyncTask(
                TaskTypeEnum.POOL_ADD_NODE.getName(),
                TaskTypeEnum.POOL_ADD_NODE.getType(),
                TaskStatusEnum.QUEUE.getStatus(),
                taskMessage,
                JSONUtil.toJsonStr(storagePoolNodeManageDto));

        if (taskService.save(task)) {
            return addNodeStepTask(task.getId(), storagePoolNodeManageDto);
        }

        log.error(TASK_CREATE_FAILED, JSONUtil.toJsonStr(task));
        return false;
    }

    @Override
    public boolean removeNode(StoragePoolNodeManageDTO storagePoolNodeManageDto) {
        if (ObjectUtils.isEmpty(storagePoolNodeManageDto)) {
            log.error(NOT_BE_EMPTY);
            return false;
        }

        String taskMessage = TaskTypeEnum.POOL_REMOVE_NODE.getName() + ":" + storagePoolNodeManageDto.getPoolName();
        if (!taskContext.validateTask(TaskTypeEnum.POOL_REMOVE_NODE, taskMessage)) {
            log.warn(TASK_ALREADY_EXIST, taskMessage);
            throw new DsmsEngineException(ResultCode.TASK_EXIST);
        }
        Task task = new AsyncTask(
                TaskTypeEnum.POOL_REMOVE_NODE.getName(),
                TaskTypeEnum.POOL_REMOVE_NODE.getType(),
                TaskStatusEnum.QUEUE.getStatus(),
                TaskTypeEnum.POOL_REMOVE_NODE.getName() + ":" + storagePoolNodeManageDto.getPoolName(),
                JSONUtil.toJsonStr(storagePoolNodeManageDto));

        if (taskService.save(task)) {
            return removeNodeStepTask(task.getId(), storagePoolNodeManageDto);
        }

        log.error(TASK_CREATE_FAILED, JSONUtil.toJsonStr(task));
        return false;

    }

    @Override
    public boolean addDisk(StoragePoolDiskManageDTO storagePoolDiskManageDto) {
        if (ObjectUtils.isEmpty(storagePoolDiskManageDto)) {
            log.error(NOT_BE_EMPTY);
            return false;
        }

        String taskMessage = TaskTypeEnum.POOL_ADD_DISK.getName() + ":" + storagePoolDiskManageDto.getPoolName();
        if (!taskContext.validateTask(TaskTypeEnum.POOL_ADD_DISK, taskMessage)) {
            log.warn(TASK_ALREADY_EXIST, taskMessage);
            throw new DsmsEngineException(ResultCode.TASK_EXIST);
        }
        //check if osd has pgs
        try {
            OsdDfResult osdDfResult = nodeApi.getOsdDf(RemoteCallUtil.generateRemoteRequest());
            List<Device> osdDfDevice = Device.osdDfResultParseDevice(osdDfResult);
            List<Integer> osdIds = storagePoolDiskManageDto.getOsd().getOsdIds();
            for (Integer osdId : osdIds) {
                Optional<Device> first = osdDfDevice.stream().filter(osdDevice -> osdDevice.getOsdId().equals(osdId)).findFirst();
                if (first.isPresent() && first.get().getDevicePgs() > PG_NUM_ZERO) {
                    throw new DsmsEngineException(ResultCode.POOL_OSD_PG_NOT_EMPTY);
                }
            }
        } catch (DsmsEngineException e) {
            throw e;
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        Task task = new AsyncTask(
                TaskTypeEnum.POOL_ADD_DISK.getName(),
                TaskTypeEnum.POOL_ADD_DISK.getType(),
                TaskStatusEnum.QUEUE.getStatus(),
                taskMessage,
                JSONUtil.toJsonStr(storagePoolDiskManageDto));

        if (taskService.save(task)) {
            return addDiskStepTask(task.getId(), storagePoolDiskManageDto);
        }

        log.error(TASK_CREATE_FAILED, JSONUtil.toJsonStr(task));
        return false;
    }

    @Override
    public boolean removeDisk(StoragePoolDiskManageDTO storagePoolDiskManageDto) {
        if (ObjectUtils.isEmpty(storagePoolDiskManageDto)) {
            log.error(NOT_BE_EMPTY);
            return false;
        }
        validateRemoveOsd(storagePoolDiskManageDto);
        String taskMessage = TaskTypeEnum.POOL_REMOVE_DISK.getName() + ":" + storagePoolDiskManageDto.getPoolName();
        if (!taskContext.validateTask(TaskTypeEnum.POOL_REMOVE_DISK, taskMessage)) {
            log.warn(TASK_ALREADY_EXIST, taskMessage);
            throw new DsmsEngineException(ResultCode.TASK_EXIST);
        }
        Task task = new AsyncTask(
                TaskTypeEnum.POOL_REMOVE_DISK.getName(),
                TaskTypeEnum.POOL_REMOVE_DISK.getType(),
                TaskStatusEnum.QUEUE.getStatus(),
                TaskTypeEnum.POOL_REMOVE_DISK.getName() + ":" + storagePoolDiskManageDto.getPoolName(),
                JSONUtil.toJsonStr(storagePoolDiskManageDto));

        if (taskService.save(task)) {
            return removeDiskStepTask(task.getId(), storagePoolDiskManageDto);
        }

        log.error(TASK_CREATE_FAILED, JSONUtil.toJsonStr(task));
        return false;
    }

    @Override
    public boolean deletePool(StoragePoolDeleteDTO storagePoolDeleteDto) {
        if (ObjectUtils.isEmpty(storagePoolDeleteDto)) {
            log.error("storagePoolDeleteDto can not be empty");
            return false;
        }
        //get the pool info
        StoragePool storagePool = get(storagePoolDeleteDto.getPoolName());
        String[] rbdList = storagePool.getRbdList();
        String fileSystem = storagePool.getFileSystem();
        String poolType = StoragePoolTypeEnum.getPoolType(storagePool.getType());
        String taskMessage = TaskTypeEnum.DELETE_POOL.getName() + ":" + storagePoolDeleteDto.getPoolName() + ", 存储池类型:" + poolType;
        if (!taskContext.validateTask(TaskTypeEnum.DELETE_POOL, taskMessage)) {
            log.warn(TASK_ALREADY_EXIST, taskMessage);
            throw new DsmsEngineException(ResultCode.TASK_EXIST);
        }
        if (!ArrayUtil.isEmpty(rbdList) || !CharSequenceUtil.isBlank(fileSystem)) {
            throw new DsmsEngineException(ResultCode.POOL_ALREADY_IN_USE);
        }
        Task task = new AsyncTask(
                TaskTypeEnum.DELETE_POOL.getName(),
                TaskTypeEnum.DELETE_POOL.getType(),
                TaskStatusEnum.QUEUE.getStatus(),
                taskMessage,
                JSONUtil.toJsonStr(storagePoolDeleteDto));

        if (taskService.save(task)) {
            return deletePoolStepTask(task.getId(), storagePoolDeleteDto);
        }

        log.error("the task for delete storage pool has been created failed");
        return false;
    }

    /*
        create pool step tasks base the  main task
     */
    private boolean createStepTask(int taskId, String poolName, String poolType) {
        //1.create root bucket step
        Step createBucketStep = new AsyncStep(
                taskId,
                StepTypeEnum.CREATE_ROOT_BUCKET.getName(),
                StepTypeEnum.CREATE_ROOT_BUCKET.getType(),
                StepTypeEnum.CREATE_ROOT_BUCKET.getName() + ":" + poolName,
                TaskStatusEnum.QUEUE.getStatus());

        if (!stepService.save(createBucketStep)) {
            log.error("the task for creating bucket has been created failed");
            return false;
        }

        //2.create the storage pool steps depend on pool type
        if (ObjectUtils.nullSafeEquals(poolType, StoragePoolTypeEnum.REPLICATED.getType())) {
            //2.1 create rule step
            Step createRuleStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.CREATE_RULE.getName(),
                    StepTypeEnum.CREATE_RULE.getType(),
                    StepTypeEnum.CREATE_RULE.getName() + ":" + poolName,
                    TaskStatusEnum.QUEUE.getStatus());

            if (!stepService.save(createRuleStep)) {
                log.error("the task for creating rule has been created failed");
                return false;
            }

            //2.2 create replicated pool step
            Step createPoolStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.CREATE_REPLICATED_POOL.getName(),
                    StepTypeEnum.CREATE_REPLICATED_POOL.getType(),
                    StepTypeEnum.CREATE_REPLICATED_POOL.getName() + ":" + poolName,
                    TaskStatusEnum.QUEUE.getStatus());

            return stepService.save(createPoolStep);
        } else if (ObjectUtils.nullSafeEquals(poolType, StoragePoolTypeEnum.ERASURE.getType())) {
            //2.1 create ec-profile step
            Step createEcProfileStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.CREATE_ECPROFILE.getName(),
                    StepTypeEnum.CREATE_ECPROFILE.getType(),
                    StepTypeEnum.CREATE_ECPROFILE.getName() + ":" + poolName,
                    TaskStatusEnum.QUEUE.getStatus());

            if (!stepService.save(createEcProfileStep)) {
                log.error("the task for creating ec-profile has been created failed");
                return false;
            }

            //2.2 create erasure pool step
            Step createErasureStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.CREATE_ERASURE_POOL.getName(),
                    StepTypeEnum.CREATE_ERASURE_POOL.getType(),
                    StepTypeEnum.CREATE_ERASURE_POOL.getName() + ":" + poolName,
                    TaskStatusEnum.QUEUE.getStatus()
            );

            if (!stepService.save(createErasureStep)) {
                log.error("the task for creating erasure pool has been created failed");
                return false;
            }

            //2.3 allow ec overwrites
            Step allowEcOverWritesStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.ALLOW_EC_OVER_WRITES.getName(),
                    StepTypeEnum.ALLOW_EC_OVER_WRITES.getType(),
                    StepTypeEnum.ALLOW_EC_OVER_WRITES.getName() + ":" + poolName,
                    TaskStatusEnum.QUEUE.getStatus()
            );
            return stepService.save(allowEcOverWritesStep);
        }

        return false;
    }

    /*
        pool add node step tasks base the  main task
     */
    private boolean addNodeStepTask(int taskId, StoragePoolNodeManageDTO storagePoolNodeManageDto) {
        String poolName = storagePoolNodeManageDto.getPoolName();
        for (String nodeName : storagePoolNodeManageDto.getNodeNames()) {
            Step createBucketStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.CREATE_HOST_BUCKET.getName(),
                    StepTypeEnum.CREATE_HOST_BUCKET.getType(),
                    StepTypeEnum.CREATE_HOST_BUCKET.getName() + ":" + poolName + CrushmapConst.BUCKET_NAME_CONNECTOR + nodeName,
                    TaskStatusEnum.QUEUE.getStatus(),
                    nodeName);
            if (!stepService.save(createBucketStep)) {
                log.error("the task for creating bucket has been created failed");
                return false;
            }
        }
        return true;
    }

    /*
        pool remove node step tasks base the  main task
     */
    private boolean removeNodeStepTask(int taskId, StoragePoolNodeManageDTO storagePoolNodeManageDto) {
        String poolName = storagePoolNodeManageDto.getPoolName();
        String nodeName = storagePoolNodeManageDto.getNodeName();
        Step deleteBucketStep = new AsyncStep(
                taskId,
                StepTypeEnum.DELETE_HOST_BUCKET.getName(),
                StepTypeEnum.DELETE_HOST_BUCKET.getType(),
                StepTypeEnum.DELETE_HOST_BUCKET.getName() + ":" + poolName + CrushmapConst.BUCKET_NAME_CONNECTOR + nodeName,
                TaskStatusEnum.QUEUE.getStatus(),
                nodeName);
        if (!stepService.save(deleteBucketStep)) {
            log.error("the task for delete bucket has been created failed");
            return false;
        }
        return true;
    }

    /*
        add disk step tasks base the  main task
     */
    private boolean addDiskStepTask(int taskId, StoragePoolDiskManageDTO storagePoolDiskManageDto) {
        String poolName = storagePoolDiskManageDto.getPoolName();
        for (int osdid : storagePoolDiskManageDto.getOsd().getOsdIds()) {
            Step addDiskStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.POOL_ADD_DISK.getName(),
                    StepTypeEnum.POOL_ADD_DISK.getType(),
                    StepTypeEnum.POOL_ADD_DISK.getName() + ":" + poolName + CrushmapConst.BUCKET_NAME_CONNECTOR + poolName,
                    TaskStatusEnum.QUEUE.getStatus(),
                    String.valueOf(osdid));
            if (!stepService.save(addDiskStep)) {
                log.error("the task for pool add disk has been created failed");
                return false;
            }
        }
        return true;
    }

    /*
        remove disk step tasks base the  main task
     */
    private boolean removeDiskStepTask(int taskId, StoragePoolDiskManageDTO storagePoolDiskManageDto) {
        String poolName = storagePoolDiskManageDto.getPoolName();
        for (int osdid : storagePoolDiskManageDto.getOsd().getOsdIds()) {
            Step removeDiskStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.POOL_REMOVE_DISK.getName(),
                    StepTypeEnum.POOL_REMOVE_DISK.getType(),
                    StepTypeEnum.POOL_REMOVE_DISK.getName() + ":" + poolName + CrushmapConst.BUCKET_NAME_CONNECTOR + poolName,
                    TaskStatusEnum.QUEUE.getStatus(),
                    String.valueOf(osdid));
            if (!stepService.save(removeDiskStep)) {
                log.error("the task for pool remove disk has been created failed");
                return false;
            }
        }
        return true;
    }

    /*
       delete pool step tasks base the  main task
     */
    private boolean deletePoolStepTask(int taskId, StoragePoolDeleteDTO storagePoolDeleteDTO) {
        //1.move osd to it's default node
        Step moveOsdToDefaultNodeStep = new AsyncStep(
                taskId,
                StepTypeEnum.RETURN_OSD_TO_NODE.getName(),
                StepTypeEnum.RETURN_OSD_TO_NODE.getType(),
                StepTypeEnum.RETURN_OSD_TO_NODE.getName() + ":" + storagePoolDeleteDTO.getPoolName(),
                TaskStatusEnum.QUEUE.getStatus());

        if (!stepService.save(moveOsdToDefaultNodeStep)) {
            log.error("the task for return osd to default node has been created failed");
            return false;
        }

        //2.delete pool step
        Step deletePoolStep = new AsyncStep(
                taskId,
                StepTypeEnum.DELETE_POOL.getName(),
                StepTypeEnum.DELETE_POOL.getType(),
                StepTypeEnum.DELETE_POOL.getName() + ":" + storagePoolDeleteDTO.getPoolName(),
                TaskStatusEnum.QUEUE.getStatus());

        if (!stepService.save(deletePoolStep)) {
            log.error("the task for delete pool has been created failed");
            return false;
        }

        //3.delete rule step
        Step deleteRuleStep = new AsyncStep(
                taskId,
                StepTypeEnum.DELETE_RULE.getName(),
                StepTypeEnum.DELETE_RULE.getType(),
                StepTypeEnum.DELETE_RULE.getName() + ":" + storagePoolDeleteDTO.getPoolName(),
                TaskStatusEnum.QUEUE.getStatus());

        if (!stepService.save(deleteRuleStep)) {
            log.error("the task for delete rule has been created failed");
            return false;
        }

        //4.delete all bucket of this pool
        Step deleteBucketStep = new AsyncStep(
                taskId,
                StepTypeEnum.DELETE_POOL_BUCKET.getName(),
                StepTypeEnum.DELETE_POOL_BUCKET.getType(),
                StepTypeEnum.DELETE_POOL_BUCKET.getName() + ":" + storagePoolDeleteDTO.getPoolName(),
                TaskStatusEnum.QUEUE.getStatus());

        if (!stepService.save(deleteBucketStep)) {
            log.error("the task for return osd to default node has been created failed");
            return false;
        }

        //5.delete ec-profile while pool type is erasure
        if (Objects.equals(storagePoolDeleteDTO.getPoolType(), StoragePoolTypeEnum.ERASURE.getCode())) {
            Step deleteEcProfileStep = new AsyncStep(
                    taskId,
                    StepTypeEnum.DELETE_ECPROFILE.getName(),
                    StepTypeEnum.DELETE_ECPROFILE.getType(),
                    StepTypeEnum.DELETE_ECPROFILE.getName() + ":" + storagePoolDeleteDTO.getPoolName(),
                    TaskStatusEnum.QUEUE.getStatus());

            if (!stepService.save(deleteEcProfileStep)) {
                log.error("the task for return osd to default node has been created failed");
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean isPoolAvailable(String poolName) {
        try {
            CrushmapBucket crushmapBuckets = crushmapApi.getCrushmapBuckets(RemoteCallUtil.generateRemoteRequest());
            List<CrushmapBucket.BucketsDTO> buckets = crushmapBuckets.getBuckets();

            List<CrushmapBucket.BucketsDTO> hostBuckets = buckets.stream()
                    .filter(bucket -> (bucket.getName().startsWith(poolName + CrushmapConst.BUCKET_NAME_CONNECTOR)
                            && !bucket.getName().contains(CrushmapConst.AUTO_GENERATED_BUCKET_NAME_CONNECTOR)))
                    .collect(Collectors.toList());
            if (hostBuckets.isEmpty()) {
                return false;
            }
            List<CrushmapBucket.BucketsDTO> hostWithOsds = hostBuckets.stream().filter(host -> !host.getItems().isEmpty()).collect(Collectors.toList());
            Rados rados = RadosSingleton.INSTANCE.getRados();
            int poolId;
            try (IoCTX ioctx = rados.ioCtxCreate(poolName)) {
                poolId = (int) ioctx.getId();
                ResponseStoragePool responsePool = storagePoolApi.getStoragePoolByPoolId(RemoteCallUtil.generateRemoteRequest(), poolId);
                if (hostWithOsds.size() < responsePool.getCrushOsdNum()) {
                    return false;
                }
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    /**
     * validate if the pool or node are exists
     *
     * @param poolName  pool's name
     * @param nodeNames list of node name
     */
    public void validateAddNode(String poolName, List<String> nodeNames) {
        try {
            //validate if pool exists
            CrushmapBucket crushmapBuckets = crushmapApi.getCrushmapBuckets(RemoteCallUtil.generateRemoteRequest());
            List<CrushmapBucket.BucketsDTO> buckets = crushmapBuckets.getBuckets();
            List<CrushmapBucket.BucketsDTO> hostBuckets = buckets.stream()
                    .filter(bucket -> Objects.equals(poolName, bucket.getName()))
                    .collect(Collectors.toList());

            if (hostBuckets.isEmpty()) {
                throw DsmsEngineException.exceptionWithMessage("pool does not exists", ResultCode.POOL_ADDNODE_ERROR);
            }

            //validate if nodes exists
            List<ResponseMon> monList = nodeApi.getNodeList(RemoteCallUtil.generateRemoteRequest());
            if (monList.isEmpty()) {
                throw DsmsEngineException.exceptionWithMessage("cluster has no nodes", ResultCode.POOL_ADDNODE_ERROR);
            }
            List<String> nodes = monList.stream().map(ResponseMon::getName).collect(Collectors.toList());
            if (!new HashSet<>(nodes).containsAll(nodeNames)) {
                throw DsmsEngineException.exceptionWithMessage("nodes does not exists", ResultCode.POOL_ADDNODE_ERROR);
            }
        } catch (DsmsEngineException e) {
            log.error(e.getMessage(), e);
            throw e;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    /*
     * check if the current osd is allowed to be removed
     *
     * when the osd is removed from the storage pool, dsms-storage will balance the data of the pg on the osd.
     * if there is no redundant osd, the removed osd will always save the original pg.
     * therefore, the removal of osd must be allowed only when there is a redundant osd.
     *
     * the following two situations allow the removal of the osd removal operation：
     * 1.when the number of crushmap nodes in the storage pool is less than or equal to the number of copies in the storage pool,
     *   determine whether there are other osds on the node to be deleted, and allow it, otherwise it is not allowed.
     * 2.when the number of crushmap nodes in the storage pool is greater than the number of copies in the storage pool,
     *   determine whether the number of nodes containing osd is greater than the number of copies, and if it is larger, it is allowed, otherwise it is not allowed.
     *
     * */
    public void validateRemoveOsd(StoragePoolDiskManageDTO storagePoolDiskManageDTO) {
        String poolName = storagePoolDiskManageDTO.getPoolName();
        String nodeName = storagePoolDiskManageDTO.getOsd().getNodeName();
        int removeOsdNum = storagePoolDiskManageDTO.getOsd().getOsdIds().size();
        String bucketNameOfRemovingOsd = poolName + CrushmapConst.BUCKET_NAME_CONNECTOR + nodeName;
        try {
            //get crushmap to obtain the pool used hosts with osds
            CrushmapBucket crushmapBuckets = crushmapApi.getCrushmapBuckets(RemoteCallUtil.generateRemoteRequest());
            List<CrushmapBucket.BucketsDTO> buckets = crushmapBuckets.getBuckets();
            List<CrushmapBucket.BucketsDTO> hostBuckets = buckets.stream()
                    .filter(bucket -> (bucket.getName().startsWith(poolName + CrushmapConst.BUCKET_NAME_CONNECTOR)
                            && !bucket.getName().contains(CrushmapConst.AUTO_GENERATED_BUCKET_NAME_CONNECTOR)))
                    .collect(Collectors.toList());
            List<CrushmapBucket.BucketsDTO> hostWithOsds = hostBuckets.stream().filter(host -> !host.getItems().isEmpty()).collect(Collectors.toList());

            Rados rados = RadosSingleton.INSTANCE.getRados();
            int poolId;
            try (IoCTX ioctx = rados.ioCtxCreate(poolName)) {
                //1.get the crushOsdNum of storage pool
                poolId = (int) ioctx.getId();
                ResponseStoragePool responsePool = storagePoolApi.getStoragePoolByPoolId(RemoteCallUtil.generateRemoteRequest(), poolId);
                int poolCrushOsdNum = responsePool.getCrushOsdNum();
                //2.validate if it is legal to remove osds
                if (hostWithOsds.size() <= poolCrushOsdNum) {
                    CrushmapBucket.BucketsDTO bucketsOfRemovingOsd = hostBuckets.stream().filter(host -> Objects.equals(host.getName(), bucketNameOfRemovingOsd)).findFirst().orElse(null);
                    assert bucketsOfRemovingOsd != null;
                    //after the osd is removed, the storage pool does not use the osd on the current node
                    if (bucketsOfRemovingOsd.getItems().size() - removeOsdNum < AVAILABLE_POOL_MIN_USED_OSD_NUM_IN_ONE_HOST) {
                        throw DsmsEngineException.exceptionWithMessageAndData("移除磁盘后存储池磁盘数小于冗余模式数量", ResultCode.POOL_REMOVEDISK_ERROR, NOT_ALLOWED_REMOVE_OSD_REASON);
                    }
                } else {
                    if (hostWithOsds.size() - REMOVED_HOST_NUM < poolCrushOsdNum) {
                        throw DsmsEngineException.exceptionWithMessageAndData("移除磁盘后存储池磁盘数小于冗余模式数量", ResultCode.POOL_REMOVEDISK_ERROR, NOT_ALLOWED_REMOVE_OSD_REASON);
                    }
                }
            }
        } catch (DsmsEngineException e) {
            throw e;
        } catch (Throwable e) {
            log.error(ResultCode.POOL_REMOVEDISK_ERROR.getMessage() + StrPool.COLON + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }


    @Override
    public boolean purgePool(String poolName) {
        if (ObjectUtils.isEmpty(poolName)) {
            return false;
        }
        Rados rados = RadosSingleton.INSTANCE.getRados();

        try (IoCTX ioctx = rados.ioCtxCreate(poolName);) {
            for (String listObjects : ioctx.listObjects()) {
                ioctx.remove(listObjects);
            }
        } catch (DsmsEngineException e) {
            log.error(e.getMessage(), e);
            throw e;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        return true;
    }
}
